﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
using Network;

namespace Managers
{
    public class NovaLoopBase
    {
        private readonly List<IHeartbeat> _heartbeatableList = new List<IHeartbeat>();
        private uint _mFrameCounter;
        private double _mLastHeartbeat;

        public double Now
        {
            get
            {
                var dnow = NetTime.Now;
                var now = (float)dnow;
                return now;
            }
        }

        protected virtual void Heartbeat()
        {
            var dnow = NetTime.Now;
            var now = (float) dnow;

            var delta = dnow - _mLastHeartbeat;

            var maxChbpS = 1000 - _heartbeatableList.Count;
            if (maxChbpS < 250)
                maxChbpS = 250;

            if (delta > 1.0/maxChbpS || delta < 0.0) // max connection heartbeats/second max
            {
                _mFrameCounter++;
                _mLastHeartbeat = dnow;

                foreach (var heartbeat in _heartbeatableList)
                {
                    heartbeat.Heartbeat(now, _mFrameCounter);
                }
            }
        }

        protected void AddHeartbeatable(IHeartbeat heartbeat)
        {
            if (!_heartbeatableList.Contains(heartbeat))
            {
                _heartbeatableList.Add(heartbeat);
            }
        }

        #region 心跳相关代码

        private Thread _mLoopThread;

        protected void StartApploop()
        {
            _mLoopThread = new Thread(Loop)
            {
                Name = "",
                IsBackground = true
            };
            _mLoopThread.Start();
        }

        private bool _flagRun;


        private void Loop()
        {
            _flagRun = true;
            do
            {
                try
                {
                    Heartbeat();
                }
                catch (Exception)
                {
                    // ignored
                }
            } while (_flagRun && NativeMethods.AppStillIdle);
        }

        public void StopLoop()
        {
            _flagRun = false;
        }

        #endregion
    }

    public static class NetTime
    {
#if IS_STOPWATCH_AVAILABLE
		private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
		private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;

		/// <summary>
		/// Get number of seconds since the application started
		/// </summary>
		public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
#else
        private static readonly uint s_timeInitialized = (uint) Environment.TickCount;

        /// <summary>
        ///     Get number of seconds since the application started
        /// </summary>
        public static double Now
        {
            get { return ((uint) Environment.TickCount - s_timeInitialized)/1000.0; }
        }
#endif

        /// <summary>
        ///     Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
        /// </summary>
        public static string ToReadable(double seconds)
        {
            if (seconds > 60)
                return TimeSpan.FromSeconds(seconds).ToString();
            return (seconds*1000.0).ToString("N2") + " ms";
        }
    }

    public static class NativeMethods
    {
        public const int WM_VSCROLL = 277; // Vertical scroll
        public const int SB_BOTTOM = 7; // Scroll to bottom 

        public static bool AppStillIdle
        {
            get
            {
                PeekMsg msg;
                return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

        [SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(out PeekMsg msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax,
            uint flags);

        [StructLayout(LayoutKind.Sequential)]
        public struct PeekMsg
        {
            public IntPtr hWnd;
            public Message msg;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public Point p;
        }
    }
}